import {
  Component,
  OnInit,
  ViewEncapsulation,
  ChangeDetectionStrategy,
  Input,
  SimpleChanges,
  OnChanges,
  TemplateRef,
  Output,
  EventEmitter,
} from '@angular/core';
import { differenceBy as _differenceBy, cloneDeep as _cloneDeep } from 'lodash';
import { InputBoolean, TypeUtils, InputNumber, LvSizeLSD } from '../core/api';
import { PaginatorComponent } from '../pagination/api';
import {
  SelectionFactor,
  TransferBelong,
  TransferColumnItem,
  ModeType,
  TransferModeType,
  TransferTypeEnum,
  TransferModeTypeEnum,
} from './transfer.interface';
import { TransferService } from './transfer.service';
import { TransferTableComponent } from './transfer-table.component';

@Component({
  selector: 'lv-transfer',
  templateUrl: './transfer.component.html',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [TransferService],
  host: {
    class: 'lv-transfer',
    '[class.lv-transfer-default]': 'lvSize === "default"',
    '[class.lv-transfer-large]': 'lvSize === "large"',
    '[class.lv-transfer-small]': 'lvSize === "small"',
  },
})
export class TransferComponent implements OnInit, OnChanges {
  @Input() lvDataKey = 'id'; // 唯一标识，用于data被选中的比较

  @Input() @InputNumber() lvTotal;

  @Input() @InputBoolean() lvAsync;

  @Input() lvSize: LvSizeLSD = 'default'; // size

  @Input() @InputNumber() lvItemSize = 10; // 每页条数

  @Input() @InputBoolean() lvShowTotal = true;

  @Output() lvChange = new EventEmitter<any>();

  @Input() @InputBoolean() lvShowOperation = false;

  @Input() lvTransferMode: TransferModeType = 'copy';

  @Input() lvSourceColumns: TransferColumnItem[] = [];

  @Input() lvTargetColumns: TransferColumnItem[] = [];

  @Input() @InputBoolean() lvColDisplayControl = true; // 是否显示列显示隐藏配置

  @Input() lvSourceData: Array<{ [key: string]: any }> = [];

  @Input() lvTargetData: Array<{ [key: string]: any }> = [];

  @Input() @InputBoolean() lvShowSelectionTotal = true;

  @Input() lvMode: ModeType = TransferModeTypeEnum.DEFAULT;

  @Input() lvSourceSelection: Array<{ [key: string]: any }> = [];

  @Input() lvTargetSelection: Array<{ [key: string]: any }> = [];

  @Input() lvTitles: Array<string | TemplateRef<void> | null>;

  @Input() lvHeaders: Array<TemplateRef<{ $implicit: any }> | null>;

  @Input() lvRenders: Array<TemplateRef<{ $implicit: any }> | null>;

  @Input() lvFooters: Array<TemplateRef<{ $implicit: any }> | null>;

  @Output() lvSelectionChange = new EventEmitter<SelectionFactor>();

  @Output() lvStateChange = new EventEmitter<{ [key: string]: any }>(); // 状态变化回调

  @Output() lvSourceColDisplayChange = new EventEmitter<{
    currentCol: TransferColumnItem;
    displayCols: TransferColumnItem[];
  }>();

  @Output() lvTargetColDisplayChange = new EventEmitter<{
    currentCol: TransferColumnItem;
    displayCols: TransferColumnItem[];
  }>();

  @Input() lvOperateTemplate: Array<TemplateRef<{
    $implicit: Array<{ [key: string]: any }>;
    selection: Array<{ [key: string]: any }>;
    paginator: PaginatorComponent;
    panel: TransferTableComponent;
  }> | null>;

  prevVisible = false;
  nextVisible = false;

  sourceData: any[] = [];
  targetData: any[] = [];
  toTargetActive = false;
  toSourceActive = false;

  public typeUtils = TypeUtils;

  simpleBelong: TransferBelong = 'source';

  public transferTypeEnum = TransferTypeEnum;
  public transferModeTypeEnum = TransferModeTypeEnum;
  titles: Array<string | TemplateRef<void> | null> = ['Title', 'Selected'];

  constructor(private service: TransferService) {
    this.service.setParentComponent(this);
  }

  moveToSource = (): void => this.moveTo('target');

  moveToTarget = (): void => this.moveTo('source');

  ngOnInit(): void {
    this.sourceData = this.lvSourceData;
  }

  /**
   * 精简模式，动态切换当前面板归属
   * @param belong 面板归属
   */
  belongChange(belong): void {
    this.simpleBelong = belong;
  }

  /**
   * 列表状态变更
   */
  stateChange(params, belong): void {
    this.lvStateChange.emit({ belong, params });
  }

  /**
   * 移除当前项
   * @param item 被移除数据
   */
  removeItem(item): void {
    if (!this.lvShowOperation) {
      this.autoMove(item, 'target', true);
    }

    // NEED_DO: 移除targetData是否作为selection变更
    this.lvSelectionChange.emit({
      belong: 'source',
      selection: this.lvSourceSelection,
    });
  }

  /**
   * 选中项变更
   * @param selection 选中项
   * @param belong 当前操作项属于源数据还是目标数据
   */
  selectionChange(selection, belong: TransferBelong): void {
    if (belong === 'source') {
      this.lvSourceSelection = selection;
    }

    this.lvSelectionChange.emit({
      belong,
      selection,
    });

    if (!this.lvShowOperation) {
      this.autoMove(selection, belong);
    }

    this.updateOperationStatus(belong);
  }

  /**
   * 点击切换按钮移动数据
   * @param belong 当前操作项属于
   */
  moveTo(belong: TransferBelong): void {
    const oppositeBelong = belong === 'source' ? 'target' : 'source'; // 对面属于
    const dataSource = belong === 'source' ? _cloneDeep(this.lvSourceSelection) : _cloneDeep(this.lvTargetSelection);

    if (this.lvTransferMode === 'copy') {
      this.copyTo(oppositeBelong, dataSource);
    }

    this.updateOperationStatus(belong);
    this.prevVisible = false;
    this.nextVisible = false;

    this.lvChange.emit({
      sourceData: this.sourceData,
      sourceSelection: this.lvSourceSelection,
      targetData: this.targetData,
      targetSelection: this.lvTargetSelection,
    });
  }

  /**
   * item移动
   * @param data 选中项
   * @param belong 当前操作项属于
   * @param remove 是否是移除操作
   */
  autoMove(data, belong: TransferBelong, remove = false): void {
    if (belong === 'source') {
      this.targetData = [...data];
    } else if (remove && belong === 'target') {
      const selectMap = new Map();

      if (TypeUtils.isArray(data)) {
        this.lvTargetSelection = [...data];
      } else {
        this.lvTargetSelection = [...[data]];
      }

      this.lvSourceSelection.forEach((select): Map<any, any> => selectMap.set(select[this.lvDataKey], select));

      this.lvTargetSelection.forEach((item): void => {
        if (selectMap.get(item[this.lvDataKey])) {
          selectMap.delete(item[this.lvDataKey]);
        }
      });

      this.lvSourceSelection = [...selectMap.values()];
      this.targetData = [...this.lvSourceSelection];
    }

    this.lvChange.emit({
      sourceData: this.sourceData,
      sourceSelection: this.lvSourceSelection,
      targetData: this.targetData,
      targetSelection: this.lvTargetSelection,
    });
  }

  ngOnChanges(changes: SimpleChanges): void {
    const {
      lvSourceData: sourceDataChange,
      lvTargetData: targetDataChange,
      lvSourceSelection: sourceSelectionChange,
      lvTitles: titlesChange,
    } = changes;

    if (sourceDataChange) {
      this.sourceData = sourceDataChange.currentValue ?? [];
    }

    if (targetDataChange) {
      this.targetData = targetDataChange.currentValue ?? [];

      if (this.lvTransferMode === 'copy') {
        // 自动移动的场景下，targetData = sourceSelection
        this.lvSourceSelection = targetDataChange.currentValue;
      }
    }

    if (sourceSelectionChange) {
      if (this.lvTransferMode === 'copy') {
        // 自动移动的场景下，targetData = sourceSelection
        !this.lvShowOperation && (this.targetData = sourceSelectionChange.currentValue ?? []);
      }

      this.updateOperationStatus('source');
    }

    if (titlesChange) {
      this.titles[0] = titlesChange.currentValue[0] ? titlesChange.currentValue[0] : 'Title';
      this.titles[1] = titlesChange.currentValue[1] ? titlesChange.currentValue[1] : 'Selected';
    }
  }

  private getSelectionData(belong): any {
    return belong === 'source' ? _cloneDeep(this.lvSourceSelection) : _cloneDeep(this.lvTargetSelection);
  }

  private updateOperationStatus(belong: TransferBelong): void {
    if (!this.lvShowOperation) {
      return;
    }

    const dataSource = belong === 'source' ? this.sourceData : this.targetData;
    this[belong === 'source' ? 'toTargetActive' : 'toSourceActive'] =
      dataSource.length && !!this.getSelectionData(belong).filter((s): boolean => !s.disabled).length;
  }

  /**
   * copy模式数据移动
   * @param oppositeBelong 对面属于
   * @param dataSource 操作的数据
   */
  private copyTo(oppositeBelong, dataSource): void {
    if (oppositeBelong === 'target') {
      // 把数据从左边（源）栏复制到右边（目标）栏；左边（源）栏保留已复制的条目，并置灰
      this.targetData = [...this.targetData, ...dataSource];
      this.lvSourceSelection = _differenceBy(this.lvSourceSelection, dataSource, this.lvDataKey);
    } else {
      // 右边（目标）栏数据移除选中项
      const moveData = _differenceBy(this.targetData, dataSource, this.lvDataKey);
      this.targetData = [...moveData];
      this.lvTargetSelection.length = 0;
    }

    // 源数据的disabled状态需要更新，避免全选时对disabled状态的数据进行操作
    // 把数据从右边（目标）栏中移除；被移除的数据在左边（源）栏中可继续操作
    const cacheData = this.sourceData;
    cacheData.forEach((item): void => {
      if (dataSource.some((d): boolean => d[this.lvDataKey] === item[this.lvDataKey])) {
        item.disabled = oppositeBelong === 'target';
      }
    });
    this.sourceData = [...cacheData];

    this.lvSelectionChange.emit({
      belong: oppositeBelong === 'target' ? 'source' : 'target',
      selection: this.lvSourceSelection,
    });
  }
}
